package server

import (
	"bytes"
	"fmt"
	"gitee.com/baidugoogle/rtsp-server/protocol/rtcpsender"
	"github.com/pion/rtcp"
	"github.com/pion/rtp"
	"github.com/pion/transport/packetio"
	"io"
	"log"
	"net"
	"time"

	"gitee.com/baidugoogle/rtsp-server/protocol/base"
	"gitee.com/baidugoogle/rtsp-server/protocol/headers"
)

type PlayerTrack struct {
	id     int32
	player *StreamPlayer

	control    string
	ssrc       uint32                 // 拉取流ssrc
	rtcpSender *rtcpsender.RTCPSender // 发送者报告

	track *Track

	inTransport    *headers.Transport
	rtpClientPort  int // 发送rtp流, rtsp setup 时设置, 没有设置, 说明客户端没有请求此路流
	rtcpClientPort int // 发送rtcp流, rtsp setup 时设置

	rtpAddr  *net.UDPAddr
	rtcpAddr *net.UDPAddr

	rtpBuffer      *packetio.Buffer
	rtpBufferIndex uint64
}

func NewPlayTrack(control string, player *StreamPlayer, track *Track) (*PlayerTrack, error) {
	c := &PlayerTrack{
		id:      GetId(),
		control: control,
		player:  player,
		ssrc:    getSSRC(),

		track: track,

		rtpBuffer: packetio.NewBuffer(),
	}

	clockRate, _ := c.track.ClockRate()
	c.rtcpSender = rtcpsender.New(time.Second*10, clockRate, func(packet rtcp.Packet) {
		c.writeRTCP(packet)
	})

	go c.run()

	return c, nil
}

func (c *PlayerTrack) Close() {
	_ = c.rtpBuffer.Close()
}

func (c *PlayerTrack) GetSSRC() uint32 {
	return c.ssrc
}

func (c *PlayerTrack) run() {

	packetBuffer := make([]byte, 1500)

	for {
		n, err := c.rtpBuffer.Read(packetBuffer)
		if err != nil {
			if err == io.EOF {
				return
			}

			log.Printf("read error %v", err)
			return
		}

		if n <= 0 {
			continue
		}

		// 需要发送的rtp packet
		data := packetBuffer[:n]

		var packet rtp.Packet
		// 修改ssrc
		if true {
			if err = packet.Unmarshal(data); err != nil {
				log.Println(err)
				continue
			}

			packet.SSRC = c.GetSSRC()
			if data, err = packet.Marshal(); err != nil {
				log.Println(err)
				continue
			}
		}

		if err = c.writeRTP(&packet, data); err != nil {
			log.Println(err)
			continue
		}
	}
}

func (c *PlayerTrack) writeRTP(packet *rtp.Packet, rtpPacketData []byte) error {

	c.rtcpSender.ProcessPacketRTP(time.Now(), packet, true)

	// log.Printf("play protocol %v", c.inTransport.Protocol)
	if c.inTransport.Protocol == headers.TransportProtocolTCP {
		frameData := MarshalInterleavedFrame(c.inTransport.InterleavedIDs[0], rtpPacketData)
		_, err := c.player.session.conn.Write(frameData)
		return err
	}

	if c.rtpAddr == nil {
		addr, ok := c.player.session.GetClientAddress()
		if !ok {
			return net.ErrWriteToConnected
		}

		c.rtpAddr = &net.UDPAddr{
			IP:   addr.IP,
			Port: c.rtpClientPort,
		}
	}

	if c.rtpAddr != nil {
		_, err := c.GetRtpSender().WriteToUDP(rtpPacketData, c.rtpAddr)
		return err
	}

	return io.ErrUnexpectedEOF
}

func (c *PlayerTrack) writeRTCP(packet rtcp.Packet) {
	rtcpData, err := packet.Marshal()
	if err != nil {
		return
	}

	if c.inTransport.Protocol == headers.TransportProtocolTCP {
		payload, _ := packet.Marshal()
		_, _ = c.player.session.conn.Write(MarshalInterleavedFrame(c.inTransport.InterleavedIDs[0], payload))
		return
	}

	// 向接收端发送 sender report
	if c.rtcpAddr == nil {
		addr, ok := c.player.session.GetClientAddress()
		if !ok {
			return
		}

		c.rtcpAddr = &net.UDPAddr{
			IP:   addr.IP,
			Port: c.rtcpClientPort,
		}
	}

	if c.rtcpAddr != nil {
		_, _ = c.GetRtcpSender().WriteToUDP(rtcpData, c.rtcpAddr)
	}
}

func (c *PlayerTrack) OnRtcpPacket(packet *RtcpPacket) {

	switch pkt := packet.Packet.(type) {
	case *rtcp.ReceiverReport:
		// 收到接收者报告
		_ = pkt.Header()
	case *rtcp.PictureLossIndication:
		// 关键帧丢失
	case *rtcp.SliceLossIndication:
		// slice丢失
	}
}

func (c *PlayerTrack) GetRtpSender() *net.UDPConn {
	return c.player.session.server.GetRtpSender()
}

func (c *PlayerTrack) GetRtcpSender() *net.UDPConn {
	return c.player.session.server.GetRtcpSender()
}

func (c *PlayerTrack) GetRtpClientAddr() (string, bool) {
	addr, ok := c.player.session.GetClientAddress()
	if !ok {
		return "", false
	}

	return fmt.Sprintf("%v:%v", addr.IP.String(), c.rtpClientPort), true
}

func (c *PlayerTrack) GetRtcpClientAddr() (string, bool) {
	addr, ok := c.player.session.GetClientAddress()
	if !ok {
		return "", false
	}

	return fmt.Sprintf("%v:%v", addr.IP.String(), c.rtcpClientPort), true
}

// MarshalInterleavedFrame rtsp over tcp
func MarshalInterleavedFrame(interleavedID int, payload []byte) []byte {
	var (
		buf   bytes.Buffer
		frame = base.InterleavedFrame{
			Channel: interleavedID,
			Payload: payload,
		}
	)

	frame.Write(&buf)

	return buf.Bytes()
}
